package com.itheima.prize.msg;

import com.itheima.prize.commons.config.RedisKeys;
import com.itheima.prize.commons.db.entity.*;
import com.itheima.prize.commons.db.mapper.CardGameMapper;
import com.itheima.prize.commons.db.mapper.CardGameProductMapper;
import com.itheima.prize.commons.db.mapper.CardGameRulesMapper;
import com.itheima.prize.commons.db.mapper.GameLoadMapper;
import com.itheima.prize.commons.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.*;

/**
 * @program: prize
 * @description: 这个是定时任务, 去拉取所有的活动信息
 * @author: zhanghz001
 * @create: 2021-06-15 16:54
 **/
// @Component
@Slf4j
public class ZhzGameTask {
    @Autowired
    private CardGameMapper gameMapper;
    
    @Autowired
    private CardGameRulesMapper gameRulesMapper;
    
    @Autowired
    private CardGameProductMapper gameProductMapper;
    
    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private GameLoadMapper gameLoadMapper;
    
    @Scheduled(cron = "0 0/1 * * * ? *")
    public void getGame() {
        //当前时间
        Date now = new Date();
        //查询将来一分钟要开始的活动
        CardGameExample example = new CardGameExample();
        //开始时间大于当前时间
        CardGameExample.Criteria criteria = example.createCriteria();
        criteria.andStarttimeGreaterThan(now);
        //开始时间小于当前时间加一分钟
        criteria.andEndtimeLessThanOrEqualTo(DateUtils.addMinutes(now, 1));
        
        //查到未来一分钟需要预热的活动列表
        List<CardGame> gameList = gameMapper.selectByExample(example);
        if (gameList.size() == 0) {
            //没有查到要开始的活动
            log.info("game list scan : size =0 ");
            return;
        }
        log.info("game list scan : size ={}", gameList.size());
        
        //将活动进行预热,放到redis中
        
        gameList.forEach(game -> {
            //当前活动开始时间
            long start = game.getStarttime().getTime();
            //当前活动结束时间
            long end = game.getEndtime().getTime();
            //计算redis活动对应的key过期时间
            long expire = (end - now.getTime()) / 1000;
            //活动持续时间
            long duration = end - start;
            
            //活动基本信息
            game.setStatus(1);
            redisUtil.set(RedisKeys.INFO + game.getId(), game, -1);
            log.info("load game info: {} ,{} ,{},{}", game.getId(), game.getTitle(), game.getStarttime(),
                    game.getEndtime());
            
            //活动奖品信息
            List<CardProductDto> products = gameLoadMapper.getByGameId(game.getId());
            HashMap<Integer, CardProduct> productMap = new HashMap<>(products.size());
            products.forEach(p -> productMap.put(p.getId(), p));
            log.info("load product size:{}", productMap.size());
            
            // 奖品数量信息
            CardGameProductExample productExample = new CardGameProductExample();
            productExample.createCriteria().andGameidEqualTo(game.getId());
            List<CardGameProduct> gameProducts = gameProductMapper.selectByExample(productExample);
            log.info("load product total amount : {}", gameProducts.size());
            
            //令牌桶
            List<Long> tokenList = new ArrayList<>();
            gameProducts.forEach(cgp -> {
                for (int i = 0; i < cgp.getAmount(); i++) {
                    //生成start到end之间的随机值
                    long rnd = start + new Random().nextInt((int) duration);
                    //为了防止random重复,扩大1000倍,并且加上一个1000内的随机值,这样的令牌就重复就很小了
                    //后面需要以相同的方式解码
                    long token = rnd * 1000 + new Random().nextInt(1000);
                    tokenList.add(token);
                    log.info("token -> product :{} -> {}", token, productMap.get(cgp.getId()).getName());
                    
                    //以令牌为key,商品为value,创建redis缓存
                    redisUtil.set(RedisKeys.TOKEN + game.getId() + "_" + token, productMap.get(cgp.getProductid()), expire);
                }
            });
            
            //排序后放到redis队列中
            Collections.sort(tokenList);
            log.info("load tokens:{}", tokenList);
            
            //从右侧压入队列,从左到右,时间戳依次增大
            redisUtil.rightPushAll(RedisKeys.TOKENS + game.getId(), tokenList);
            redisUtil.expire(RedisKeys.TOKENS + game.getId(), expire);
            
            //奖品策略配置信息
            CardGameRulesExample rulesExample = new CardGameRulesExample();
            rulesExample.createCriteria().andGameidEqualTo(game.getId());
            List<CardGameRules> rules = gameRulesMapper.selectByExample(rulesExample);
            
            //遍历策略,存入redis hset
            rules.forEach(r -> {
                redisUtil.hset(RedisKeys.MAXGOAL + game.getId(), r.getUserlevel() + "", r.getGoalTimes());
                redisUtil.hset(RedisKeys.MAXENTER + game.getId(), r.getUserlevel() + "", r.getEnterTimes());
                redisUtil.hset(RedisKeys.RANDOMRATE + game.getId(), r.getUserlevel() + "", r.getRandomRate());
            });
            redisUtil.expire(RedisKeys.MAXENTER + game.getId(), expire);
            redisUtil.expire(RedisKeys.MAXGOAL + game.getId(), expire);
            redisUtil.expire(RedisKeys.RANDOMRATE + game.getId(), expire);
            
            //活动更新成已经预热,禁止再次随便改动
            game.setStatus(1);
            gameMapper.updateByPrimaryKey(game);
        });
    }
    
}
